package com.xiaoshuidi.cloud.module.finance.service.finance.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.xiaoshuidi.cloud.framework.common.exception.ServiceException;
import com.xiaoshuidi.cloud.framework.common.pojo.CommonResult;
import com.xiaoshuidi.cloud.framework.common.pojo.PageResult;
import com.xiaoshuidi.cloud.framework.common.util.json.JsonUtils;
import com.xiaoshuidi.cloud.framework.mybatis.core.util.MyBatisUtils;
import com.xiaoshuidi.cloud.framework.redis.lock.DistributeLock;
import com.xiaoshuidi.cloud.framework.redis.sequence.TimebaseSequenceGenerator;
import com.xiaoshuidi.cloud.framework.security.core.LoginUser;
import com.xiaoshuidi.cloud.module.bpm.api.task.BpmProcessInstanceApi;
import com.xiaoshuidi.cloud.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
import com.xiaoshuidi.cloud.module.bpm.enums.BpmBusinessTypeEnum;
import com.xiaoshuidi.cloud.module.bpm.enums.task.BpmProcessInstanceResultEnum;
import com.xiaoshuidi.cloud.module.bpm.mq.message.BpmProcessInstanceResultEvent;
import com.xiaoshuidi.cloud.module.contract.api.ApprovalApi;
import com.xiaoshuidi.cloud.module.contract.api.ContractApi;
import com.xiaoshuidi.cloud.module.contract.api.ContractRoomAccountApi;
import com.xiaoshuidi.cloud.module.contract.enums.ApproveTypeEnum;
import com.xiaoshuidi.cloud.module.contract.vo.ContractRoommateApiVO;
import com.xiaoshuidi.cloud.module.contract.vo.CustomerVO;
import com.xiaoshuidi.cloud.module.finance.api.finance.dto.BankTransferReqDTO;
import com.xiaoshuidi.cloud.module.finance.controller.admin.finance.vo.account.*;
import com.xiaoshuidi.cloud.module.finance.controller.app.finance.vo.AppFinanceAccountWithdrawPageReqVO;
import com.xiaoshuidi.cloud.module.finance.controller.app.finance.vo.AppFinanceAccountWithdrawReqVO;
import com.xiaoshuidi.cloud.module.finance.controller.app.finance.vo.AppFinanceAccountWithdrawReviewReqVO;
import com.xiaoshuidi.cloud.module.finance.convert.finance.FinanceConvert;
import com.xiaoshuidi.cloud.module.finance.dal.dataobject.finance.FinanceAccountDO;
import com.xiaoshuidi.cloud.module.finance.dal.dataobject.finance.FinanceAccountWithdrawDO;
import com.xiaoshuidi.cloud.module.finance.dal.dataobject.finance.FinanceAccountWithdrawExtDO;
import com.xiaoshuidi.cloud.module.finance.dal.mysql.finance.FinanceAccountMapper;
import com.xiaoshuidi.cloud.module.finance.dal.mysql.finance.FinanceAccountWithdrawMapper;
import com.xiaoshuidi.cloud.module.finance.dal.redis.RedisKeyConstants;
import com.xiaoshuidi.cloud.module.finance.enums.ErrorCodeConstants;
import com.xiaoshuidi.cloud.module.finance.enums.finance.*;
import com.xiaoshuidi.cloud.module.finance.service.finance.FinanceAccountService;
import com.xiaoshuidi.cloud.module.finance.service.finance.FinanceAccountWithdrawService;
import com.xiaoshuidi.cloud.module.finance.service.finance.FinanceBankTransferService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

import static com.xiaoshuidi.cloud.framework.security.core.util.SecurityFrameworkUtils.getLoginUser;
import static com.xiaoshuidi.cloud.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;

@Slf4j
@Service
public class FinanceAccountWithdrawServiceImpl implements FinanceAccountWithdrawService {
    @Resource
    private FinanceAccountService financeAccountService;
    @Resource
    private FinanceAccountWithdrawMapper financeAccountWithdrawMapper;
    @Resource
    private FinanceAccountMapper financeAccountMapper;
    @Resource
    private ContractRoomAccountApi contractRoomAccountApi;
    @Resource
    private ContractApi contractApi;
    @Resource
    private TransactionTemplate transactionTemplate;
    @Resource
    private FinanceBankTransferService financeBankTransferService;
    @Resource
    private TimebaseSequenceGenerator timebaseSequenceGenerator;
    @Value("${spring.application.name}")
    private String applicationName;
    @Resource
    private BpmProcessInstanceApi bpmProcessInstanceApi;
    @Resource
    private ApprovalApi approvalApi;

    @Override
    public void withdraw(LoginUser user, AppFinanceAccountWithdrawReqVO reqVO) {
        try (DistributeLock lock = getLock(reqVO.getCustId(), FinanceCustomerTypeEnum.ROOM.name())) {
            if (StrUtil.isNotBlank(reqVO.getPayeeBankAccount())) {
                reqVO.setPayeeBankAccount(reqVO.getPayeeBankAccount().replaceAll("\\s", ""));
            }
            if (!lock.tryLock()) {
                throw new ServiceException(ErrorCodeConstants.FINANCE_ACCOUNT_CONCURRENT_MODIFY);
            }
            FinanceAccountDO account = financeAccountService.get(reqVO.getCustId(),
                    FinanceCustomerTypeEnum.ROOM.name());
            if (account == null) {
                throw new ServiceException(ErrorCodeConstants.FINANCE_ACCOUNT_NOT_EXISTS);
            }
            if (account.getBalance().compareTo(reqVO.getAmount()) < 0) {
                throw new ServiceException(ErrorCodeConstants.FINANCE_ACCOUNT_NOT_ENOUGH);
            }
            CommonResult<List<CustomerVO>> result = contractRoomAccountApi.getAllCompanyCheckinRoom(user.getId());
            if (result.isError() || CollectionUtils.isEmpty(result.getData())) {
                throw new ServiceException(ErrorCodeConstants.FINANCE_ACCOUNT_NOT_EXISTS);
            }
            CustomerVO v = result.getData().stream().filter(c -> String.format("%d_%d", c.getCompanyId(), c.getRoomId()).equals(reqVO.getCustId()))
                    .findFirst().orElse(null);
            if (v == null) {
                throw new ServiceException(ErrorCodeConstants.FINANCE_ACCOUNT_NOT_EXISTS);
            }
            CommonResult<List<ContractRoommateApiVO>> roommate = contractApi.getRoommateByRoomId(v.getRoomId());
            if (roommate.isError() || CollectionUtils.isEmpty(roommate.getData())) {
                throw new ServiceException(ErrorCodeConstants.FINANCE_ROOM_ACCOUNT_ROOMMATE_NOT_FOUND);
            }
            transactionTemplate.executeWithoutResult(status -> {
                FinanceAccountDO update = FinanceAccountDO.builder()
                        .id(account.getId())
                        .frozen(account.getFrozen().add(reqVO.getAmount()))
                        .balance(account.getBalance().subtract(reqVO.getAmount()))
                        .build();
                financeAccountMapper.updateById(update);
                FinanceAccountWithdrawDO.FinanceAccountWithdrawDOBuilder builder = FinanceAccountWithdrawDO.builder();
                String no = timebaseSequenceGenerator.YYYYMMDDHHMMSS.next("lease:withdraw:no");
                builder.accountId(account.getId())
                        .custId(account.getCustId())
                        .number("TX" + no)
                        .custType(account.getCustType())
                        .companyId(Integer.parseInt(account.getProp().get("companyId").toString()))
                        .companyName(account.getProp().get("companyName").toString())
                        .apartmentId(Integer.parseInt(account.getProp().get("apartmentId").toString()))
                        .apartmentName(account.getProp().get("apartmentName").toString())
                        .roomId(Long.parseLong(account.getProp().get("roomId").toString()))
                        .roomName(account.getCustName())
                        .beginningBalance(account.getBalance())
                        .endingBalance(account.getBalance().subtract(reqVO.getAmount()))
                        .applyMemberId(user.getId())
                        .applyMemberName(user.getNickname())
                        .applyMemberPhone(user.getMobile())
                        .result(FinanceAccountWithdrawResultEnum.SUBMIT.name())
                        .type(FinanceAccountWithdrawTypeEnum.APPLY.name())
                        .source(FinanceAccountTypeSourceEnum.APP.name());
                FinanceConvert.INSTANCE.convert(reqVO, builder);

                List<FinanceAccountWithdrawDO> l = roommate.getData().stream().filter(r -> !r.getMemberId().equals(user.getId())).map(r ->
                                FinanceAccountWithdrawDO.builder()
                                        .accountId(account.getId())
                                        .custId(account.getCustId())
                                        .custType(account.getCustType())
                                        .number("TX" + no)
                                        .companyId(Integer.parseInt(account.getProp().get("companyId").toString()))
                                        .companyName(account.getProp().get("companyName").toString())
                                        .apartmentId(Integer.parseInt(account.getProp().get("apartmentId").toString()))
                                        .apartmentName(account.getProp().get("apartmentName").toString())
                                        .roomId(Long.parseLong(account.getProp().get("roomId").toString()))
                                        .roomName(account.getCustName())
                                        .beginningBalance(account.getBalance())
                                        .endingBalance(account.getBalance().subtract(reqVO.getAmount()))
                                        .applyMemberId(user.getId())
                                        .applyMemberName(user.getNickname())
                                        .applyMemberPhone(user.getMobile())
                                        .amount(reqVO.getAmount())
                                        .type(FinanceAccountWithdrawTypeEnum.REVIEW.name())
                                        .result(FinanceAccountWithdrawResultEnum.PENDING.name())
                                        .reviewMemberId(r.getMemberId())
                                        .reviewMemberName(r.getRoommateName())
                                        .reviewMemberPhone(r.getRoommatePhone())
                                        //流入app
                                        .source(FinanceAccountTypeSourceEnum.APP.name()))

                        .peek(b -> FinanceConvert.INSTANCE.convert(reqVO, b))
                        .map(FinanceAccountWithdrawDO.FinanceAccountWithdrawDOBuilder::build)
                        .collect(Collectors.toList());
                // 同住人审批 或 审批流审批
                if (!CollectionUtils.isEmpty(l)/*|| 工作流审批*/) {
//                    builder.result(FinanceAccountWithdrawResultEnum.APPROVAL_PENDING.name());
                    builder.result(FinanceAccountWithdrawResultEnum.PENDING.name());
                }
                FinanceAccountWithdrawDO obj = builder.build();
                financeAccountWithdrawMapper.insert(obj);
                if (!CollectionUtils.isEmpty(l)) {
                    l.forEach(w -> w.setRefId(obj.getId()));
                    financeAccountWithdrawMapper.insertBatch(l);
                } else {
                    //房屋仅存在自己时  查看是否需要审批
                    CommonResult<String> mark = approvalApi.getApartmentMark(Long.valueOf(account.getProp().get(
                            "apartmentId").toString()), ApproveTypeEnum.BALANCE_WITHDRAWAL.getMsg());
                    FinanceAccountWithdrawDO build = FinanceAccountWithdrawDO.builder().id(obj.getId())
                            .result(FinanceAccountWithdrawResultEnum.SUBMIT.name()).build();
                    //如果需要审批
                    if (mark.isSuccess() && org.apache.commons.lang3.StringUtils.isNotBlank(mark.getData())) {
                        //发起审批
                        build.setProcessInstanceId(createProcessInstance(mark.getData()
                                , obj, getLoginUser().getId()));
                        build.setResult(FinanceAccountWithdrawResultEnum.APPROVAL_PENDING.name());
                        financeAccountWithdrawMapper.updateById(build);
                    }


                }


            });
        }

    }

    @Override
    public PageResult<FinanceAccountWithdrawDO> withdrawPage(LoginUser user, AppFinanceAccountWithdrawPageReqVO reqVO) {
        log.info("withdrawPage reqVO:{}", reqVO.toString());
        QueryWrapper<FinanceAccountWithdrawDO> query = new QueryWrapper<>();
        if (StringUtils.hasText(reqVO.getCustId())) {
            query.eq("cust_id", reqVO.getCustId());
        } else {
            query.eq("type", reqVO.getType());
            if (FinanceAccountWithdrawTypeEnum.APPLY.name().equals(reqVO.getType())) {
                query.eq("apply_member_id", user.getId());
            } else if (FinanceAccountWithdrawTypeEnum.REVIEW.name().equals(reqVO.getType())) {
                query.eq("review_member_id", user.getId());
            }
        }
        if (reqVO.getApartmentId() != null) {
            query.eq("apartment_id", reqVO.getApartmentId());
        }
        query.orderByDesc("id");
        return financeAccountWithdrawMapper.selectPage(reqVO, query);
    }

    @Override
    public PageResult<FinanceAccountWithdrawExtDO> withdrawPage(LoginUser user, FinanceAccountWithdrawPageReqVO reqVO) {
        PageResult<FinanceAccountWithdrawExtDO> pageResult =
                financeAccountWithdrawMapper.selectPage(reqVO);
        if (CollectionUtil.isNotEmpty(pageResult.getList())) {
            AtomicInteger index = new AtomicInteger((reqVO.getPageNo() - 1) * reqVO.getPageSize() + 1);
            pageResult.getList().forEach(f -> {
                f.setSerialNumber(index.getAndIncrement());
            });
        }
        return pageResult;
    }

    @Override
    public List<FinanceAccountWithdrawExcelVO> exportAccountWithdrawExcel(FinanceAccountWithdrawPageReqVO reqVO) {
        List<FinanceAccountWithdrawExtDO> list = new ArrayList<>();
        //一次取200页
        recursionWithdraw(reqVO, list, 200);
        List<FinanceAccountWithdrawExcelVO> financeAccountWithdrawExcelVOS =
                FinanceConvert.INSTANCE.convertToWithdrawExcelList(list);
        return financeAccountWithdrawExcelVOS;
    }

    private void recursionWithdraw(FinanceAccountWithdrawPageReqVO reqVO, List<FinanceAccountWithdrawExtDO> list,
                                   int pageSize) {
        reqVO.setPageSize(pageSize);
        IPage<FinanceAccountWithdrawExtDO> mpPage = MyBatisUtils.buildPage(reqVO);
        IPage<FinanceAccountWithdrawExtDO> financeAccountWithdrawExtDOIPage =
                financeAccountWithdrawMapper.selectWithdrawPage(reqVO, mpPage);
        if (ObjectUtil.isNotEmpty(financeAccountWithdrawExtDOIPage) && CollectionUtil.isNotEmpty(financeAccountWithdrawExtDOIPage.getRecords())) {
            list.addAll(financeAccountWithdrawExtDOIPage.getRecords());
            if (mpPage.getRecords().size() == pageSize) {
                reqVO.setPageNo(reqVO.getPageNo() + 1);
                recursionWithdraw(reqVO, list, pageSize);
            }
        }
    }

    @Override
    @Transactional
    public void review(LoginUser reviewer, AppFinanceAccountWithdrawReviewReqVO reqVO) {
        FinanceAccountWithdrawDO financeAccountWithdrawDO = financeAccountWithdrawMapper.selectById(reqVO.getId());
        if (financeAccountWithdrawDO == null || financeAccountWithdrawDO.getReviewMemberId() == null
                || !financeAccountWithdrawDO.getReviewMemberId().equals(reviewer.getId())) {
            throw new ServiceException(ErrorCodeConstants.FINANCE_ROOM_ACCOUNT_WITHDRAW_NOT_FOUND);
        }
        if (FinanceAccountWithdrawResultEnum.PENDING.name().equals(financeAccountWithdrawDO.getResult())) {
            if (!FinanceAccountWithdrawResultEnum.AGREE.name().equals(reqVO.getResult())
                    && !FinanceAccountWithdrawResultEnum.REFUSE.name().equals(reqVO.getResult())) {
                throw new ServiceException(ErrorCodeConstants.FINANCE_ROOM_ACCOUNT_WITHDRAW_RESULT_TYPE_WRONG);
            }
            try (DistributeLock lock = DistributeLock.getLock("lease:withdraw:" + financeAccountWithdrawDO.getRefId())) {
                if (!lock.tryLock()) {
                    throw new ServiceException(ErrorCodeConstants.FINANCE_ROOM_ACCOUNT_WITHDRAW_CONCURRENT_MODIFY);
                }

                FinanceAccountWithdrawDO accountWithdrawDO =
                        financeAccountWithdrawMapper.selectById(financeAccountWithdrawDO.getRefId());
                if(accountWithdrawDO == null){
                    throw new ServiceException(ErrorCodeConstants.FINANCE_ROOM_ACCOUNT_WITHDRAW_NOT_FOUND);
                }
                if(!FinanceAccountWithdrawResultEnum.PENDING.name().equals(financeAccountWithdrawDO.getResult())){
                    throw new ServiceException(ErrorCodeConstants.FINANCE_ROOM_ACCOUNT_WITHDRAW_NOT_FOUND.getCode(),"提现信息已处理请勿重复处理");
                }

                //获取账户信息 在获取锁之后
                FinanceAccountDO account = financeAccountService.get(financeAccountWithdrawDO.getCustId(), FinanceCustomerTypeEnum.ROOM.name());
                if (account == null) {
                    throw new ServiceException(ErrorCodeConstants.FINANCE_ACCOUNT_NOT_EXISTS);
                }
                log.info("同住人{}{}提现{}{} {}元", reviewer.getNickname(),
                        FinanceAccountWithdrawResultEnum.valueOf(reqVO.getResult()).getDesc(), financeAccountWithdrawDO.getRoomId(),
                        financeAccountWithdrawDO.getRoomName(), financeAccountWithdrawDO.getAmount());
                FinanceAccountWithdrawDO update = FinanceAccountWithdrawDO.builder()
                        .id(financeAccountWithdrawDO.getId())
                        .remark(reqVO.getRemark())
                        .result(reqVO.getResult())
                        .agreeTime(FinanceAccountWithdrawResultEnum.AGREE.name().equals(reqVO.getResult()) ? LocalDateTime.now() : null)
                        .build();
                financeAccountWithdrawMapper.updateById(update);
                List<FinanceAccountWithdrawDO> l = financeAccountWithdrawMapper.selectList("ref_id", financeAccountWithdrawDO.getRefId());
                if (FinanceAccountWithdrawResultEnum.AGREE.name().equals(reqVO.getResult())) {
                    // 提现申请全部通过
                    if (l.stream().allMatch(d -> FinanceAccountWithdrawResultEnum.AGREE.name().equals(d.getResult()))) {
                        /*financeAccountWithdrawMapper.updateById(FinanceAccountWithdrawDO.builder().id(financeAccountWithdrawDO.getRefId()).agreeTime(LocalDateTime.now())
                                .result(FinanceAccountWithdrawResultEnum.SUBMIT.name()).build());
                        //同意冻结钱减少
                        FinanceAccountDO updateAcount = FinanceAccountDO.builder()
                                .id(account.getId())
                                .frozen(account.getFrozen().subtract(financeAccountWithdrawDO.getAmount()))
                                .build();
                        financeAccountMapper.updateById(updateAcount);*/

                        CommonResult<String> mark = approvalApi.getApartmentMark(Long.valueOf(financeAccountWithdrawDO.getApartmentId()), ApproveTypeEnum.BALANCE_WITHDRAWAL.getMsg());
                        FinanceAccountWithdrawDO build = FinanceAccountWithdrawDO.builder().id(financeAccountWithdrawDO.getRefId()).agreeTime(LocalDateTime.now())
                                .result(FinanceAccountWithdrawResultEnum.SUBMIT.name()).build();
                        //如果需要审批
                        if (mark.isSuccess() && org.apache.commons.lang3.StringUtils.isNotBlank(mark.getData())) {
                            //发起审批
                            build.setProcessInstanceId(createProcessInstance(mark.getData()
                                    , accountWithdrawDO, getLoginUser().getId()));
                            build.setResult(FinanceAccountWithdrawResultEnum.APPROVAL_PENDING.name());
                        } else {
                            //同意冻结钱减少
//                        FinanceAccountDO updateAcount = FinanceAccountDO.builder()
//                                .id(account.getId())
//                                .frozen(account.getFrozen().subtract(financeAccountWithdrawDO.getAmount()))
//                                .build();
//                        financeAccountMapper.updateById(updateAcount);
                        }
                        financeAccountWithdrawMapper.updateById(build);

                    }
                    // 一人拒绝, 就拒绝
                } else if (FinanceAccountWithdrawResultEnum.REFUSE.name().equals(reqVO.getResult())) {
                    financeAccountWithdrawMapper.updateById(FinanceAccountWithdrawDO.builder().id(financeAccountWithdrawDO.getRefId())
                            .result(FinanceAccountWithdrawResultEnum.REFUSE.name()).build());
                    //一个人拒绝其他未处理数据直接删除
                    financeAccountWithdrawMapper.delete( new QueryWrapper<FinanceAccountWithdrawDO>().lambda()
                            .eq(FinanceAccountWithdrawDO::getRefId,financeAccountWithdrawDO.getRefId())
                            .eq(FinanceAccountWithdrawDO::getResult, FinanceAccountWithdrawResultEnum.PENDING.name()));
                    //拒绝余额回滚
                    FinanceAccountDO updateAcount = FinanceAccountDO.builder()
                            .id(account.getId())
                            .frozen(account.getFrozen().subtract(financeAccountWithdrawDO.getAmount()))
                            .balance(account.getBalance().add(financeAccountWithdrawDO.getAmount()))
                            .build();
                    financeAccountMapper.updateById(updateAcount);
                }
            }
        }
    }

    @Override
    public FinanceAccountWithdrawPageResult getWithdrawSummary(FinanceAccountWithdrawPageReqVO reqVO) {
        List<Map<String, Object>> l = financeAccountWithdrawMapper.selectConfirmSummary(reqVO);
        BigDecimal totalPaid = BigDecimal.ZERO;
        BigDecimal totalSubmit = BigDecimal.ZERO;
        BigDecimal totalWaiting = BigDecimal.ZERO;
        int totalPaidCount = 0;
        int totalSubmitCount = 0;
        int totalWaitingCount = 0;

        for (Map<String, Object> m : l) {
            Object status = m.get("result");
            Object payAmount = m.get("payAmount");
            Object count = m.get("cnt");
            if (FinanceAccountWithdrawResultEnum.SUCCESS.name().equals(status)) {
                totalPaid = new BigDecimal(payAmount.toString());
                totalPaidCount = Integer.parseInt(count.toString());
            } else if (FinanceAccountWithdrawResultEnum.WAITING.name().equals(status)) {
                totalWaiting = new BigDecimal(payAmount.toString());
                totalWaitingCount = Integer.parseInt(count.toString());
            } else if (FinanceAccountWithdrawResultEnum.SUBMIT.name().equals(status)) {
                totalSubmit = new BigDecimal(payAmount.toString());
                totalSubmitCount = Integer.parseInt(count.toString());
            }
        }
        return FinanceAccountWithdrawPageResult.builder().totalPaid(totalPaid)
                .totalPaidCount(totalPaidCount)
                .totalSubmit(totalSubmit)
                .totalSubmitCount(totalSubmitCount)
                .totalWaiting(totalWaiting)
                .totalWaitingCount(totalWaitingCount).build();
    }

    @Override
    public FinanceAccountWithdrawHeaderResult withdrawSubmitHeader() {
        QueryWrapper<FinanceAccountWithdrawDO> objectLambdaQueryWrapper = Wrappers.query();
        objectLambdaQueryWrapper
                .select("cust_type as custType,count( 1 ) AS cnt ")
                .eq("type", FinanceAccountWithdrawTypeEnum.APPLY.name())
                .eq("result", FinanceAccountWithdrawResultEnum.SUBMIT.name())
                .groupBy("cust_type");
        FinanceAccountWithdrawHeaderResult result = new FinanceAccountWithdrawHeaderResult();
        List<Map<String, Object>> maps = financeAccountWithdrawMapper.selectMaps(objectLambdaQueryWrapper);
        if (CollectionUtil.isNotEmpty(maps)) {
            maps.stream().forEach(f -> {
                Object custType = f.get("custType");
                Object cnt = f.get("cnt");
                if (FinanceCustomerTypeEnum.ROOM.name().equals(custType.toString())) {
                    result.setRoomCount(Integer.valueOf(f.get("cnt").toString()));
                } else if (FinanceCustomerTypeEnum.INDIVIDUAL.name().equals(custType.toString())) {
                    result.setIndividualCount(Integer.valueOf(f.get("cnt").toString()));
                } else if (FinanceCustomerTypeEnum.COMPANY.name().equals(custType.toString())) {
                    result.setCompanyCount(Integer.valueOf(f.get("cnt").toString()));
                }
            });
        }

        return result;
    }

    @Override
    public void withdrawConfirm(LoginUser user, FinanceWithdrawConfirmReqVO reqVO) {
        List<FinanceAccountWithdrawDO> l = financeAccountWithdrawMapper.selectBatchIds(reqVO.getId());
        if (CollectionUtils.isEmpty(l)) {
            return;
        }
        transactionTemplate.executeWithoutResult(status -> l.forEach(financeAccountWithdrawDO -> {
            if (!FinanceAccountWithdrawResultEnum.SUBMIT.name().equals(financeAccountWithdrawDO.getResult())) {
                throw new ServiceException(ErrorCodeConstants.FINANCE_ROOM_ACCOUNT_WITHDRAW_STATUS);
            }
            BankTransferReqDTO req = new BankTransferReqDTO();
            req.setBizNo(financeAccountWithdrawDO.getId().toString());
            req.setBizType(SourceBizTypeEnum.WITHDRAW.name());
            req.setBizDate(LocalDate.now());
            req.setOperator(user.getNickname());
            req.setDepartment(financeAccountWithdrawDO.getApartmentName());
            log.info("apartmentName:{},department:{}", financeAccountWithdrawDO.getApartmentName(), req.getDepartment());
            req.setApartmentId(financeAccountWithdrawDO.getApartmentId().longValue());
            req.setRoomId(financeAccountWithdrawDO.getRoomId());
            req.setRoomName(financeAccountWithdrawDO.getRoomName());
            req.setPayeeName(financeAccountWithdrawDO.getPayeeName());
            req.setPayeeMobile(financeAccountWithdrawDO.getPayeeMobile());
            req.setPayeeBank(financeAccountWithdrawDO.getPayeeBank());
            req.setPayeeBankBranch(financeAccountWithdrawDO.getPayeeBankBranch());
            req.setPayeeBankCode(financeAccountWithdrawDO.getPayeeBankCode());
            req.setPayeeBankAccount(financeAccountWithdrawDO.getPayeeBankAccount());
            req.setPayeeType("租客");
            if (FinanceCustomerTypeEnum.COMPANY.name().equals(financeAccountWithdrawDO.getCustType())) {
                req.setPayeeType("企业");
            }
            req.setPayeeBankProvince(financeAccountWithdrawDO.getPayeeBankProvince());
            req.setPayeeBankCity(financeAccountWithdrawDO.getPayeeBankCity());
            req.setPayAmount(financeAccountWithdrawDO.getAmount());
            req.setReason("余额提现");
            req.setTenantId(user.getTenantId());
            req.setDeptId(user.getDeptId());

            financeAccountWithdrawMapper.updateById(FinanceAccountWithdrawDO.builder()
                    .id(financeAccountWithdrawDO.getId())
                    .result(FinanceAccountWithdrawResultEnum.WAITING.name())
                    .confirmTime(LocalDateTime.now())
                    .build());
            String format = String.format("%s(%s)", financeAccountWithdrawDO.getApplyMemberName(),
                    financeAccountWithdrawDO.getApplyMemberPhone());
            if (!FinanceCustomerTypeEnum.ROOM.name().equals(financeAccountWithdrawDO.getCustType())) {
                //操作人
                format = financeAccountWithdrawDO.getReviewMemberName();
            }
            financeAccountService.withdraw(financeAccountWithdrawDO.getAccountId(),
                    financeAccountWithdrawDO.getCustId(),
                    financeAccountWithdrawDO.getCustType(), financeAccountWithdrawDO.getAmount(),
                    format);
            financeBankTransferService.save(req);
        }));
    }

    private String createProcessInstance(String mark, FinanceAccountWithdrawDO financeAccountWithdrawDO, Long userId) {
        // 工作流参数
        BpmProcessInstanceCreateReqDTO bpmProcessInstanceCreateReqDTO = new BpmProcessInstanceCreateReqDTO();
        bpmProcessInstanceCreateReqDTO.setProcessDefinitionKey(mark);
        bpmProcessInstanceCreateReqDTO.setBusinessType(BpmBusinessTypeEnum.BALANCE_WITHDRAWAL.name());
        bpmProcessInstanceCreateReqDTO.setBusinessKey(String.valueOf(financeAccountWithdrawDO.getId()));
        Map<String, Object> variables = new HashMap<>();
        variables.put("reviewMemberName", financeAccountWithdrawDO.getReviewMemberName());
        variables.put("createName", financeAccountWithdrawDO.getApplyMemberName());
        variables.put("reviewMemberPhone", financeAccountWithdrawDO.getReviewMemberPhone());
        variables.put("roomName", financeAccountWithdrawDO.getApartmentName() + financeAccountWithdrawDO.getRoomName());
        variables.put("roomId", financeAccountWithdrawDO.getRoomId());
        bpmProcessInstanceCreateReqDTO.setVariables(variables);
        bpmProcessInstanceCreateReqDTO.setTenantId(getLoginUser().getTenantId());
        bpmProcessInstanceCreateReqDTO.setServiceName(applicationName);

        BpmProcessInstanceCreateReqDTO.ExtendDto extendDto = new BpmProcessInstanceCreateReqDTO.ExtendDto();
        extendDto.setRoomId(Objects.nonNull(financeAccountWithdrawDO.getRoomId()) ?
                financeAccountWithdrawDO.getRoomId().toString() : null);
//        extendDto.setContractId(contractRoomDO.getId());
//        extendDto.setContractNo(contractRoomDO.getContractNo());
        extendDto.setRoomName(financeAccountWithdrawDO.getRoomName());
        extendDto.setApartmentId(financeAccountWithdrawDO.getApartmentId() != null ?
                Long.valueOf(financeAccountWithdrawDO.getApartmentId()) : null);
        extendDto.setApartmentName(financeAccountWithdrawDO.getApartmentName());
        extendDto.setCustomName(financeAccountWithdrawDO.getApplyMemberName());
        extendDto.setCustomPhone(financeAccountWithdrawDO.getApplyMemberPhone());
        bpmProcessInstanceCreateReqDTO.setExtendDto(extendDto);

        log.info("processInstance userId {}, request : {}", userId,
                JsonUtils.toJsonString(bpmProcessInstanceCreateReqDTO));
        CommonResult<String> processInstance = bpmProcessInstanceApi.createProcessInstance(userId,
                bpmProcessInstanceCreateReqDTO);
        log.info("processInstance:{}", JsonUtils.toJsonString(processInstance));
        return processInstance.getCheckedData();

    }

    @Override
    public FinanceAccountWithdrawApprovalDetails getById(Long id) {
        //主数据 主住人
        FinanceAccountWithdrawDO financeAccountWithdrawDO = financeAccountWithdrawMapper.selectById(id);
        FinanceAccountWithdrawApprovalDetails withdrawApprovalDetails =
                new FinanceAccountWithdrawApprovalDetails();
        BeanUtil.copyProperties(financeAccountWithdrawDO, withdrawApprovalDetails);
        //提现人
        List<FinanceAccountWithdrawApprovalMember> memberList = CollectionUtil.newArrayList(
                FinanceAccountWithdrawApprovalMember.builder()
                        .isWithdrawalBy(true)
                        .memberPhone(financeAccountWithdrawDO.getApplyMemberPhone())
                        .memberName(financeAccountWithdrawDO.getApplyMemberName())
                        .time(financeAccountWithdrawDO.getCreateTime())
                        .build()
        );
        //同住人
        List<FinanceAccountWithdrawDO> financeAccountWithdrawDOS =
                financeAccountWithdrawMapper.selectList(new LambdaQueryWrapper<FinanceAccountWithdrawDO>().eq(FinanceAccountWithdrawDO::getRefId, id)
                        .orderByDesc(FinanceAccountWithdrawDO::getId));
        if (CollectionUtil.isNotEmpty(financeAccountWithdrawDOS)) {
            memberList.addAll(financeAccountWithdrawDOS.stream().map(m -> {
                return FinanceAccountWithdrawApprovalMember.builder()
                        .isWithdrawalBy(false)
                        .memberPhone(m.getReviewMemberPhone())
                        .memberName(m.getReviewMemberName())
                        .time(m.getAgreeTime())
                        .build();
            }).collect(Collectors.toList()));
        }

        withdrawApprovalDetails.setMemberList(memberList);
        return withdrawApprovalDetails;
    }

    @Override
    public void approvalResult(BpmProcessInstanceResultEvent event) {
        FinanceAccountWithdrawDO financeAccountWithdrawDO = financeAccountWithdrawMapper.selectById(event.getBusinessKey());
        try (DistributeLock lock = getRechargeLock(financeAccountWithdrawDO.getCustId(), FinanceCustomerTypeEnum.ROOM.name())) {
            if (!lock.tryLock()) {
                throw new ServiceException(ErrorCodeConstants.FINANCE_ROOM_ACCOUNT_WITHDRAW_CONCURRENT_MODIFY);
            }
            FinanceAccountDO account = financeAccountService.get(financeAccountWithdrawDO.getCustId(), FinanceCustomerTypeEnum.ROOM.name());
            if (BpmProcessInstanceResultEnum.APPROVE.getResult().equals(event.getResult())) {

                //同意冻结钱减少
//                FinanceAccountDO updateAcount = FinanceAccountDO.builder()
//                        .id(account.getId())
//                        .frozen(account.getFrozen().subtract(financeAccountWithdrawDO.getAmount()))
//                        .build();
//                financeAccountMapper.updateById(updateAcount);
                FinanceAccountWithdrawDO build =
                        FinanceAccountWithdrawDO.builder().id(financeAccountWithdrawDO.getId()).agreeTime(LocalDateTime.now())
                                .result(FinanceAccountWithdrawResultEnum.SUBMIT.name()).build();
                financeAccountWithdrawMapper.updateById(build);

            } else {
                //拒绝
                financeAccountWithdrawMapper.updateById(FinanceAccountWithdrawDO.builder().id(financeAccountWithdrawDO.getId())
                        .result(FinanceAccountWithdrawResultEnum.REJECT.name()).build());
                //拒绝余额回滚
                FinanceAccountDO updateAcount = FinanceAccountDO.builder()
                        .id(account.getId())
                        .frozen(account.getFrozen().subtract(financeAccountWithdrawDO.getAmount()))
                        .balance(account.getBalance().add(financeAccountWithdrawDO.getAmount()))
                        .build();
                financeAccountMapper.updateById(updateAcount);
            }

        }


    }

    @Override
    public void updateMemberUser(Long userId, String userName, String mobile, Long oldUserId) {
        financeAccountWithdrawMapper.updateApplyMemberUser(userId, userName, mobile, oldUserId);
        financeAccountWithdrawMapper.updateReviewMemberUser(userId, userName, mobile, oldUserId);
    }

    private DistributeLock getLock(String custId, String custType) {
        return DistributeLock.getLock((RedisKeyConstants.ACCOUNT_LOCK.formatKey(custId, custType)));
    }

    private DistributeLock getRechargeLock(String custId, String custType) {
        return DistributeLock.getLock((RedisKeyConstants.RECHARGE_LOCK.formatKey(custId, custType)));
    }
}
